home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 2 / Atari Mega Archive CD - Volume 2.iso / minix / up1510b.tgz / up1510b / src / commands / paste.c < prev    next >
C/C++ Source or Header  |  1990-07-23  |  12KB  |  430 lines

  1. /* paste - laminate files        Author: David Ihnat */
  2.  
  3. /* Paste - a recreation of the Unix(Tm) paste(1) command.
  4.  *
  5.  * syntax:    paste file1 file2 ...
  6.  *        paste -dLIST file1 file2 ...
  7.  *        paste -s [-dLIST] file1 file2 ...
  8.  *
  9.  *    Copyright (C) 1984 by David M. Ihnat
  10.  *
  11.  * This program is a total rewrite of the Bell Laboratories Unix(Tm)
  12.  * command of the same name, as of System V.  It contains no proprietary
  13.  * code, and therefore may be used without violation of any proprietary
  14.  * agreements whatsoever.  However, you will notice that the program is
  15.  * copyrighted by me.  This is to assure the program does *not* fall
  16.  * into the public domain.  Thus, I may specify just what I am now:
  17.  * This program may be freely copied and distributed, provided this notice
  18.  * remains; it may not be sold for profit without express written consent of
  19.  * the author.
  20.  * Please note that I recreated the behavior of the Unix(Tm) 'paste' command
  21.  * as faithfully as possible, with minor exceptions (noted below); however,
  22.  * I haven't run a full set of regression * tests.  Thus, the user of
  23.  * this program accepts full responsibility for any effects or loss;
  24.  * in particular, the author is not responsible for any losses,
  25.  * explicit or incidental, that may be incurred through use of this program.
  26.  *
  27.  * The changes to the program, with one exception, are transparent to
  28.  * a user familiar with the Unix command of the same name.  These changes
  29.  * are:
  30.  *
  31.  * 1) The '-s' option had a bug in the Unix version when used with multiple
  32.  *    files.  (It would repeat each file in a list, i.e., for
  33.  *    'paste -s file1 file2 file3', it would list
  34.  *    <file1\n><file1\n><file2\n><file1\n><file2\n><file3\n>
  35.  *    I fixed this, and reported the bug to the providers of the command in
  36.  *    Unix.
  37.  *
  38.  * 2) The list of valid escape sequences has been expanded to include
  39.  *    \b,\f, and \r.  (Just because *I* can't imagine why you'd want
  40.  *    to use them doesn't mean I should keep them from you.)
  41.  *
  42.  * 3) There is no longer any restriction on line length.
  43.  *
  44.  * I ask that any bugs (and, if possible, fixes) be reported to me when
  45.  * possible.  -David Ihnat (312) 784-4544 ihuxx!ignatz
  46.  */
  47.  
  48. /* Modified to run under MINIX 1.1
  49.  * by David O. Tinker  (416) 978-3636 (utgpu!dtinker)
  50.  * Sept. 19, 1987
  51.  */
  52.  
  53. #include <errno.h>
  54. #include <ctype.h>
  55. #include <stdio.h>
  56.  
  57. extern int errno;
  58.  
  59.  
  60. /* I'd love to use enums, but not everyone has them.  Portability, y'know. */
  61. #define NODELIM        1
  62. #define USAGE        2
  63. #define BADFILE        3
  64. #define TOOMANY        4
  65.  
  66. #define    TAB        '\t'
  67. #define    NL        '\n'
  68. #define    BS        '\b'
  69. #define    FF        '\f'
  70. #define    CR        '\r'
  71. #define DEL             '\177'
  72.  
  73. #define    _MAXSZ        512
  74. #define _MAXFILES    12
  75. #define    CLOSED        ((FILE *)-1)
  76. #define ENDLIST        ((FILE *)-2)
  77.  
  78. char *cmdnam, ToUpper();
  79.  
  80.  
  81. short int dflag, sflag;
  82. char delims[] = {TAB};
  83.  
  84. main(argc, argv)
  85. int argc;
  86. char **argv;
  87. {
  88.   char *strcpy();
  89.  
  90.   dflag = sflag = 0;
  91.  
  92.   cmdnam = *argv;
  93.  
  94.   if (argc >= 2) {
  95.  
  96.     /* Skip invocation name */
  97.     argv++;
  98.     argc--;
  99.  
  100.     /* First, parse input options */
  101.  
  102.     while (argv[0][0] == '-' && argv[0][1] != '\0') {
  103.         switch (ToUpper(argv[0][1])) {
  104.             case 'D':
  105.             /* Delimiter character(s) */
  106.             strcpy(delims, &argv[0][2]);
  107.             if (*delims == '\0')
  108.                 prerr(NODELIM, "");
  109.             else
  110.                 delimbuild(delims);
  111.  
  112.             break;
  113.  
  114.             case 'S':    sflag++;    break;
  115.  
  116.             default:    prerr(USAGE, "");
  117.         }
  118.         argv++;
  119.         argc--;
  120.     }
  121.   } else
  122.     prerr(USAGE, "");
  123.  
  124.   /* If no files specified, simply exit.  Otherwise, if not the old
  125.    * '-s' option, process all files. If '-s', then process files
  126.    * one-at-a-time. */
  127.   if (!sflag)
  128.     docol(argc, argv);    /* Column paste */
  129.   else
  130.     doserial(argc, argv);    /* Serial paste */
  131.  
  132.   exit(0);
  133. }
  134.  
  135. docol(nfiles, fnamptr)
  136. int nfiles;
  137. char **fnamptr;
  138. {
  139.   char iobuff[_MAXSZ];        /* i/o buffer for the fgets */
  140.   short int somedone;        /* flag for blank field handling */
  141.  
  142.   /* There is a strange case where all files are just ready to be
  143.    * closed, or will on this round.  In that case, the string of
  144.    * delimiters must be preserved.  delbuf[1] ->delbuf[MAXFILES+1]
  145.    * provides intermediate storage for closed files, if needed;
  146.    * delbuf[0] is the current index. */
  147.   char delbuf[_MAXFILES + 2];
  148.  
  149.   FILE *fileptr[_MAXFILES + 1];
  150.   FILE *fopen();
  151.  
  152.   char *fgets();
  153.  
  154.   int filecnt;            /* Set to number of files to process */
  155.   register char *delimptr;    /* Cycling delimiter pointer */
  156.   int index;            /* Working variable */
  157.   int strend;            /* End of string in buffer */
  158.  
  159.   /* Perform column paste.  First, attempt to open all files. (This
  160.    * could be expanded to an infinite number of files, but at the
  161.    * (considerable) expense of remembering the file and its current
  162.    * offset, then opening/reading/closing.  The commands' utility
  163.    * doesn't warrant the effort; at least, to me...) */
  164.  
  165.   for (filecnt = 0; (nfiles > 0) && (filecnt < _MAXFILES); filecnt++, nfiles--, fnamptr++) {
  166.     if (fnamptr[0][0] == '-')
  167.         fileptr[filecnt] = stdin;
  168.     else {
  169.         fileptr[filecnt] = fopen(*fnamptr, "r");
  170.         if (fileptr[filecnt] == NULL) prerr(BADFILE, *fnamptr);
  171.     }
  172.   }
  173.  
  174.   fileptr[filecnt] = ENDLIST;    /* End of list. */
  175.  
  176.   if (nfiles) prerr(TOOMANY, "");
  177.  
  178.   /* Have all files.  Now, read a line from each file, and output to
  179.    * stdout.  Notice that the old 511 character limitation on the line
  180.    * length no longer applies, since this program doesn't do the
  181.    * buffering.  Do this until you go through the loop and don't
  182.    * successfully read from any of the files. */
  183.   for (; filecnt;) {
  184.     somedone = 0;        /* Blank field handling flag */
  185.     delimptr = delims;    /* Start at beginning of delim list */
  186.     delbuf[0] = 0;        /* No squirreled delims */
  187.  
  188.     for (index = 0; (fileptr[index] != ENDLIST) && filecnt; index++) {
  189.         /* Read a line and immediately output. If it's too
  190.          * big for the buffer, then dump what was read and go
  191.          * back for more.
  192.          * 
  193.          * Otherwise, if it is from the last file, then leave
  194.          * the carriage return in place; if not, replace with
  195.          * a delimiter (if any) */
  196.  
  197.         strend = 0;    /* Set so can easily detect EOF */
  198.  
  199.         if (fileptr[index] != CLOSED)
  200.             while (fgets(iobuff, (_MAXSZ - 1), fileptr[index]) != (char *) NULL) {
  201.                 strend = strlen(iobuff);    /* Did the buffer fill? */
  202.  
  203.                 if (strend == (_MAXSZ - 1)) {
  204.                     /* Gosh, what a long line. */
  205.                     fputs(iobuff, stdout);
  206.                     strend = 0;
  207.                     continue;
  208.                 }
  209.  
  210.                 /* Ok got whole line in buffer. */
  211.                 break;    /* Out of loop for this file */
  212.             }
  213.  
  214.         /* Ended either on an EOF (well, actually NULL
  215.          * return-- it *could* be some sort of file error,
  216.          * but but if the file was opened successfully, this
  217.          * is unlikely. Besides, error checking on streams
  218.          * doesn't allow us to decide exactly what went
  219.          * wrong, so I'm going to be very Unix-like and
  220.          * ignore it!), or a closed file, or a received line.
  221.          * If an EOF, close the file and mark it in the list.
  222.          * In any case, output the delimiter of choice. */
  223.  
  224.         if (!strend) {
  225.             if (fileptr[index] != CLOSED) {
  226.                 fclose(fileptr[index]);
  227.                 fileptr[index] = CLOSED;
  228.                 filecnt--;
  229.             }
  230.  
  231.             /* Is this the end of the whole thing? */
  232.             if ((fileptr[index + 1] == ENDLIST) && !somedone)
  233.                 continue;    /* EXITS */
  234.  
  235.             /* Ok, some files not closed this line. Last file? */
  236.             if (fileptr[index + 1] == ENDLIST) {
  237.                 if (delbuf[0]) {
  238.                     fputs(&delbuf[1], stdout);
  239.                     delbuf[0] = 0;
  240.                 }
  241.                 putc((int) NL, stdout);
  242.                 continue;    /* Next read of files */
  243.             } else {
  244.                 /* Closed file; setup delim */
  245.                 if (*delimptr != DEL) {
  246.                     delbuf[0]++;
  247.                     delbuf[delbuf[0]] = *delimptr++;
  248.                     delbuf[delbuf[0] + 1] = '\0';
  249.                 } else
  250.                     delimptr++;
  251.             }
  252.  
  253.             /* Reset end of delimiter string if necessary */
  254.             if (*delimptr == '\0') delimptr = delims;
  255.         } else {
  256.             /* Some data read. */
  257.             somedone++;
  258.  
  259.             /* Any saved delims? */
  260.             if (delbuf[0]) {
  261.                 fputs(&delbuf[1], stdout);
  262.                 delbuf[0] = 0;
  263.             }
  264.  
  265.             /* If last file, last char will be NL. */
  266.             if (fileptr[index + 1] != ENDLIST) {
  267.                 if (*delimptr == DEL) {
  268.                     delimptr++;
  269.                     iobuff[strend - 1] = '\0';    /* No delim */
  270.                 } else
  271.                     iobuff[strend - 1] = *delimptr++;
  272.             }
  273.             if (*delimptr == '\0') delimptr = delims;
  274.  
  275.             /* Now dump the buffer */
  276.             fputs(iobuff, stdout);
  277.             fflush(stdout);
  278.         }
  279.     }
  280.   }
  281. }
  282.  
  283. doserial(nf